package efg;

import interfacesFerrovia.ControleFerrovia;
import interfacesFerrovia.TratadorDeEventos;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import efg.tasks.CheckSwitchScheduleTask;
import efg.tasks.FrontalCollisionAvoidingTask;
import efg.tasks.StoppingShedule;

/**
 * 
 */

public class EventProcessor implements TratadorDeEventos {

	private Railway railway;
	private ControleFerrovia railwayControl;
	private Integer currentTime;

	public EventProcessor(Railway railway, ControleFerrovia railwayControl) {
		assert (railway != null);
		assert (railwayControl != null);
		this.railway = railway;
		this.railwayControl = railwayControl;
		currentTime = 0;
		assert (this.railway != null);
		assert (this.railwayControl != null);
	}

	@Override
	public void alarmeChave(String idChave, int instanteAtual,
			TipoFalha tipoDeFalha) {
		debug("Alarme chave: " + idChave + " " + instanteAtual + " "
				+ tipoDeFalha);
		dumpState();

		railway.stopAllTrains();
	}

	@Override
	public void alarmeComposicao(String idComposicao, int instanteAtual,
			TipoFalha tipoDeFalha) {
		debug("Alarme composição: " + idComposicao + " " + instanteAtual + " "
				+ tipoDeFalha);
		dumpState();

		railway.stopAllTrains();
	}

	@Override
	public void chegadaComposicao(String idComposicao, int instanteAtual,
			String idTrecho) {
		debug("Composicao id:" + idComposicao + " chegou ao destino no trecho "
				+ idTrecho + " no instante " + instanteAtual + ".");
		dumpState();

	}

	@Override
	public void entradaComposicaoChave(String idChave, int instanteAtual,
			String idComposicao, String idTrechoOrigem) {
		debug("Entrada Chave Composição: " + idChave + " " + instanteAtual
				+ " " + idComposicao + " " + idTrechoOrigem);
		dumpState();

	}

	@Override
	public void partidaComposicao(String idComposicao, int instanteAtual,
			String idTrecho) {
		debug("Partida Composicao: " + idComposicao + " " + instanteAtual + " "
				+ idTrecho);
		dumpState();

		Train train = (Train) getElementById(idComposicao);
		Track track = (Track) train.getCurrentPlace();

		if (train.isLastTrack(idTrecho)) {
			railway.registerTask(new StoppingShedule(train));
			return;
		}

		train.createConfictList();

		checkCollisions(instanteAtual, train, track);

	}

	private void checkCollisions(int currentTime, Train train, Track track) {
		List<String> route = train.getRouteIds();
		Integer trackIndex = route.indexOf(track.getId());
		Track nextTrack = (Track) getElementById(route.get(trackIndex + 1));

		Set<Train> conficts = nextTrack.getConflicts(train);
		if (!conficts.isEmpty()) {
			for (Train conflictedTrain : conficts) {
				if (conflictedTrain.getCurrentPlace().equals(nextTrack)) {
					Track conflictedNextTrack = conflictedTrain
							.getNextTrack((Track) conflictedTrain
									.getCurrentPlace());
					if (conflictedNextTrack != null
							&& conflictedNextTrack.equals(track)) {
						// colisão frontal inevitavel. pare todo mundo
						railway.stuntAllTrains();
						return;
					}
				}
			}

			train.setSpeed(1f, true);
			SelfCheckableTask task = new FrontalCollisionAvoidingTask(nextTrack,
					train, conficts);
			railway.registerTask(task);
		} else {
			train.checkSwitchSchedule(track, currentTime);
		}
	}

	@Override
	public void passagemComposicao(String idTrecho, int instanteAtual,
			float posicao, String idComposicao, float velocidade,
			boolean sentidoAB) {
		debug("Passagem Composição: " + idTrecho + " " + instanteAtual + " "
				+ posicao + " " + idComposicao + " " + velocidade + " "
				+ ((sentidoAB) ? "A->B" : "B->A"));
		dumpState();

	}

	@Override
	public void saidaComposicaoChave(String idChave, int instanteAtual,
			String idComposicao, String idTrechoDestino) {
		debug("Saida Composição Chave: " + idChave + " " + instanteAtual + " "
				+ idComposicao + " " + idTrechoDestino);
		dumpState();

		Train train = (Train) getElementById(idComposicao);
		Track track = (Track) getElementById(idTrechoDestino);

		if (train.isLastTrack(idTrechoDestino)) {
			train.accelerateToMax(false);
			railway.registerTask(new StoppingShedule(train));
			return;
		}

		SelfCheckableTask task = new CheckSwitchScheduleTask(train, track);
		railway.registerTask(task);
		// train.checkSwitchSchedule(track, instanteAtual);

	}

	@Override
	public void tic(int instanteAtual) {
		assert (this.currentTime != null);
		assert (this.currentTime <= instanteAtual);
		this.currentTime = instanteAtual;

		railway.executeTasks(currentTime);
	}

	protected RailwayElement getElementById(String id) {
		assert (railway != null);
		RailwayElement el = railway.getElementById(id);
		assert (el != null);
		return el;
	}

	public Collection<Switch> getSwitches() {
		assert (railway != null);
		Collection<Switch> ret = railway.getSwitches();
		assert (ret != null);
		assert (!ret.isEmpty());
		return ret;
	}

	public Collection<Train> getTrains() {
		assert (railway != null);
		Collection<Train> ret = railway.getTrains();
		assert (ret != null);
		assert (!ret.isEmpty());
		return ret;
	}

	public Collection<Track> getTraks() {
		assert (railway != null);
		Collection<Track> ret = railway.getTracks();
		assert (ret != null);
		assert (!ret.isEmpty());
		return ret;
	}

	public void dumpState() {
		railway.dumpState();
	}

	private void debug(String s) {
		System.out.println("======");
		System.out.println(s);
	}
}
